
package com.wanwang.vps.risk.controller;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.FileUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/ContinuousUpload")
public class ContinuousUpload {
    private Map<String, RandomAccessFile> randomAccessFiles = new HashMap<>();

    /**
     * 断点文件上传：直接写入最终文件，并根据当前文件大小得出已经上传大小。
     */
    @RequestMapping("/uploadFullFile")
    public Long uploadFullFile(HttpServletRequest request) {
        RandomAccessFile outputFile = null;
        String fileName = request.getHeader("fileName");
        try {
            request.setCharacterEncoding("utf-8");
            File file = new File(getUploadFolder(), fileName);
            outputFile = new RandomAccessFile(file, "rw");
            InputStream in = request.getInputStream();
            long skip = outputFile.length();
            skip = in.skip(skip);
            outputFile.seek(skip);
            byte[] b = new byte[1024];
            int n;
            closeRandomAccessFile(fileName);
            randomAccessFiles.put(fileName, outputFile);
            while ((n = in.read(b)) != -1) {
                Thread.sleep(10);//fixme
                outputFile.write(b, 0, n);
            }
            System.out.println("上传成功： " + fileName);
            return outputFile.length();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeRandomAccessFile(fileName, outputFile);
        }
        return 0L;
    }

    private File getUploadFolder() {
        return new File("D:\\test\\real\\");
    }

    /**
     * size: [ startIndex, endIndex )
     */
    private long onUpload(HttpServletRequest request, String fileName, long totalSize, long startIndex, long endIndex) {
        File file = new File(getUploadFolder(), fileName);
        long blockSize = endIndex - startIndex;
        if (file.exists() && file.length() == blockSize) {
            mergeFileInTry(file, totalSize);
            return blockSize;
        }

        RandomAccessFile outputFile = null;

        try {
            request.setCharacterEncoding("utf-8");
            outputFile = new RandomAccessFile(file, "rw");
            // 开始文件传输
            InputStream in = request.getInputStream();
            long skip = outputFile.length();
            System.out.println("已经上传：" + skip);
            skip = in.skip(startIndex + skip) - startIndex;
            if (skip < 0) throw new IllegalStateException("InputStream skip failed, value: " + (startIndex + outputFile.length()));

            outputFile.seek(skip);
            byte[] buffer = new byte[1024];
            int read;
            long size = skip;
            closeRandomAccessFile(fileName);
            randomAccessFiles.put(fileName, outputFile);
            while ((read = in.read(buffer)) != -1) {
                Thread.sleep(10);//fixme
                size += read;
                if (size < blockSize) outputFile.write(buffer, 0, read);
                else {
                    if (size > blockSize) read -= size - blockSize;
                    outputFile.write(buffer, 0, read);
                    break;
                }
            }
            System.out.println("上传成功： " + fileName);
            return outputFile.length();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeRandomAccessFile(fileName, outputFile);
            mergeFileInTry(file, totalSize);
        }
        return 0L;
    }

    private void mergeFileInTry(File patchFile, long totalSize) {
        try {
            mergeFile(patchFile, totalSize);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param patchFile 1.jar.3-1.patch => {fileName}.{blockCount}-{blockIndex}.patch
     */
    private void mergeFile(File patchFile, long totalSize) throws IOException {
        String patchFileName = patchFile.getName();
        String[] parts = patchFileName.split("\\.");
        String blockString = parts[parts.length - 2];
        String[] block = blockString.split("-");//3-1
        int blockCount = Integer.parseInt(block[0]);
        int blockIndex = Integer.parseInt(block[1]);
        File[] files = new File[blockCount];
        File folder = patchFile.getParentFile();
        String tail = blockString + ".patch";
        long size = 0;
        for (int i = 0; i < blockCount; i++) {
            if (i + 1 != blockIndex) {
                String fileName = patchFileName.replace(tail, String.format("%d-%d.patch", blockCount, i + 1));
                files[i] = new File(folder, fileName);
            } else {
                files[i] = patchFile;
            }
            if (files[i].exists()) size += files[i].length();
        }

        if (size == totalSize) {
            String fileName = patchFileName.substring(0, patchFileName.length() - tail.length() - 1);
            File file = new File(folder, fileName);
            OutputStream outputStream = new FileOutputStream(file, true);
            for (int i = 0; i < blockCount; i++) {
                FileUtils.copyFile(files[i], outputStream);
                //noinspection ResultOfMethodCallIgnored
                files[i].delete();
            }
            outputStream.close();
        }
    }

    /**
     * 文件分片上传：把文件划分为多片（Patch），每个片独立上传，所有片上传完成后再合并为最终文件。
     */
    @RequestMapping("/multiPatchUpload")
    public long multiPatchUpload(HttpServletRequest request) {
        String fileName = request.getHeader("fileName");
        File file = new File(getUploadFolder(), fileName);
        if (file.exists()) return file.length();

        long totalSize = Long.parseLong(request.getHeader("totalSize"));
        long blockSize = getBlockSize(request.getHeader("blockSize"));
        //切片序号（从1开始）
        int blockIndex = Integer.parseInt(request.getHeader("blockIndex"));
        int blockCount = getBlockCount(totalSize, blockSize);
        //1.jar.3-1.patch = {fileName}.{blockCount}-{blockIndex}.patch
        String patchFileName = String.format("%s.%d-%d.patch", fileName, blockCount, blockIndex);
        long startIndex = blockSize * (blockIndex - 1);
        long endIndex = startIndex + blockSize;
        if (endIndex > totalSize) endIndex = totalSize;
        return onUpload(request, patchFileName, totalSize, startIndex, endIndex);
    }

    private int getBlockCount(long total, long blockSize) {
        long count = total / blockSize;
        return (int) (count + (total % blockSize == 0 ? 0 : 1));
    }

    /**
     * 切片大小（不区分大小写）KB MB，eg: 500kb
     */
    private long getBlockSize(String size) {
        size = size.toLowerCase();
        String number = size.substring(0, size.length() - 2);
        long num = Long.parseLong(number);
        if (size.endsWith("kb")) {
            num *= 1024;
        } else if (size.endsWith("mb")) {
            num *= 1024 * 1024;
        } else throw new IllegalArgumentException("切片大小（不区分大小写）只支持 KB MB，收到：" + size);
        return num;
    }

    private void back(int length) {
        for (int i = 0; i < length; i++) {
            System.out.print("\b");
        }
    }

    private void closeRandomAccessFile(String fileName, RandomAccessFile file) {
        randomAccessFiles.remove(fileName);
        if (file != null) {
            try {
                file.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void closeRandomAccessFile(String fileName) {
        RandomAccessFile file = randomAccessFiles.remove(fileName);
        if (file != null) {
            try {
                file.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

	